package top.wilsonlv.jaguar.cloud.auth.arcsoft;

import com.arcsoft.face.*;
import com.arcsoft.face.toolkit.ImageFactory;
import com.arcsoft.face.toolkit.ImageInfo;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.klock.annotation.Klock;
import org.springframework.stereotype.Service;
import top.wilsonlv.jaguar.cloud.auth.sdk.dto.CredentialMatchResult;
import top.wilsonlv.jaguar.commons.data.encryption.util.EncryptionUtil;
import top.wilsonlv.jaguar.commons.web.exception.impl.CheckedException;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

/**
 * @author lvws
 * @since 2022/1/11
 */
@Service
@RequiredArgsConstructor
public class ArcFaceService {

    private final ArcProperties arcProperties;

    @Autowired(required = false)
    private FaceEngine faceEngine;

    private void checkFaceEngineEnable() {
        if (this.faceEngine == null) {
            throw new CheckedException("人脸识别功能未开启");
        }
    }

    public CredentialMatchResult compareFace(String face1, String face2) {
        FaceFeature feature1 = this.check(face1);
        FaceFeature feature2 = this.check(face2);
        return compareFeature(feature1, feature2);
    }

    public CredentialMatchResult compareFeature(FaceFeature feature1, FaceFeature feature2) {
        this.checkFaceEngineEnable();

        FaceSimilar faceSimilar = new FaceSimilar();
        faceEngine.compareFaceFeature(feature1, feature2, faceSimilar);

        CredentialMatchResult result = new CredentialMatchResult();
        result.setScore(BigDecimal.valueOf(faceSimilar.getScore()));
        result.setSuccess(faceSimilar.getScore() >= arcProperties.getCompareThreshold());
        return result;
    }


    @Klock(name = "arcFaceCheck")
    public FaceFeature check(String face) {
        return check(EncryptionUtil.decryptBase64(face), false);
    }

    @Klock(name = "arcFaceCheck")
    public FaceFeature check(String face, boolean multipleException) {
        return check(EncryptionUtil.decryptBase64(face), multipleException);
    }

    @Klock(name = "arcFaceCheck")
    public FaceFeature check(byte[] bytes, boolean multipleException) {
        this.checkFaceEngineEnable();

        //人脸检测
        ImageInfo imageInfo = ImageFactory.getRGBData(bytes);
        List<FaceInfo> faceInfoList = new ArrayList<>();
        faceEngine.detectFaces(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList);
        if (faceInfoList.size() == 0) {
            throw new CheckedException("未检测到人脸");
        }

        if (multipleException && faceInfoList.size() > 1) {
            throw new CheckedException("未检测到多张人脸");
        }

        FunctionConfiguration configuration = new FunctionConfiguration();
        configuration.setSupportAge(true);
        configuration.setSupportFace3dAngle(true);
        configuration.setSupportGender(true);
        configuration.setSupportLiveness(true);

        faceEngine.process(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(),
                imageInfo.getImageFormat(), faceInfoList, configuration);

        //活体检测
        List<LivenessInfo> livenessInfoList = new ArrayList<>();
        faceEngine.getLiveness(livenessInfoList);
        if (livenessInfoList.get(0).getLiveness() != 1) {
            throw new CheckedException("检测到目标不是活体");
        }

        //特征提取
        FaceFeature faceFeature = new FaceFeature();
        faceEngine.extractFaceFeature(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList.get(0), faceFeature);

        return faceFeature;
    }

}
